home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-04 / ddj1291.zip / STAT.ASC < prev    next >
Text File  |  1991-11-11  |  20KB  |  637 lines

  1. _STATISTICAL PERFORMANCE ANALYSIS_
  2. by Fred Motteler
  3.  
  4. [LISTING ONE]
  5.  
  6. /* patest.c -- A collection of simple routines to test the accuracy of 
  7. ** statistical performance analysis programs for the PC and ES 1800 emulator.
  8. */
  9.  
  10. /* Default delay timing parameters */
  11. #define DELAY_ONE 1
  12. #define DELAY_TWO 2
  13. #define DELAY_THREE 3
  14. #define DELAY_FOUR 4
  15. #define DELAY_FIVE 5
  16. #define DELAY_SIX 6
  17. #define DELAY_SEVEN 7
  18. #define DELAY_EIGHT 8
  19. #define DELAY_NINE 9
  20. #define DELAY_TEN 10
  21. #define DELAY_ELEVEN 11
  22. #define DELAY_TWELVE 12
  23. #define DELAY_THIRTEEN 13
  24. #define DELAY_FOURTEEN 14
  25. #define DELAY_FIFTEEN 15
  26.  
  27. #define DELAY_SCALE 10      /* Effectively muliplies values by 1000 */
  28. #define DELAY_LOOPS 5       /* Default number of times thru main loop */
  29.  
  30. /* Loop delay parameters.  These are done as globals to allow easy access to
  31. ** the timing parameters via the ES 1800 emulator.  This allows different
  32. ** timing configurations to be tested without having to recompile and link
  33. ** this code.  Kludgie, but it encourages easy experimentation. */
  34. long final_sumL = 0;
  35. int time_oneN = DELAY_ONE;
  36. int time_twoN = DELAY_TWO;
  37. int time_threeN = DELAY_THREE;
  38. int time_fourN = DELAY_FOUR;
  39. int time_fiveN = DELAY_FIVE;
  40. int time_sixN = DELAY_SIX;
  41. int time_sevenN = DELAY_SEVEN;
  42. int time_eightN = DELAY_EIGHT;
  43. int time_nineN = DELAY_NINE;
  44. int time_tenN = DELAY_TEN;
  45. int time_elevenN = DELAY_ELEVEN;
  46. int time_twelveN = DELAY_TWELVE;
  47. int time_thirteenN = DELAY_THIRTEEN;
  48. int time_fourteenN = DELAY_FOURTEEN;
  49. int time_fifteenN = DELAY_FIFTEEN;
  50. int scaleN = DELAY_SCALE;
  51. int looptoloopN = DELAY_LOOPS;
  52.  
  53. /* Function: long delay_xxxx(int delayN)
  54. ** Description: These are simple functions designed to allow varied delays.
  55. ** The code in each delay function is the identical to the code in all of 
  56. ** the other delay functions. This allows accurate comparision of the 
  57. ** relative execution time of each function. Fifteen of these functions should
  58. ** be a reasonable number to represent a simple "real" program.
  59. */
  60. long
  61. delay_one(delayN)
  62. int delayN;
  63. {
  64.     int i;
  65.     long sumL;
  66.  
  67.     sumL = 0L;
  68.     delayN <<= scaleN;
  69.     for (i = 0; i < delayN; i++)
  70.     sumL += (long) i;
  71.  
  72.     return(sumL);
  73. }
  74. long
  75. delay_two(delayN)
  76. int delayN;
  77. {
  78.     int i;
  79.     long sumL;
  80.     sumL = 0L;
  81.     delayN <<= scaleN;
  82.     for (i = 0; i < delayN; i++)
  83.     sumL += (long) i;
  84.     return(sumL);
  85. }
  86.  
  87.     .
  88.     .
  89.     .
  90.     .
  91.  
  92. long
  93. delay_fifteen(delayN)
  94. int delayN;
  95. {
  96.     int i;
  97.     long sumL;
  98.     sumL = 0L;
  99.     delayN <<= scaleN;
  100.     for (i = 0; i < delayN; i++)
  101.     sumL += (long) i;
  102.     return(sumL);
  103. }
  104.  
  105. /* Function: void main()
  106. ** Description: This is a simple routine to run the various delay routines.
  107. ** The delay time variables are all globals to allow experimentation with 
  108. ** the timing parameters using the ES 1800 emulator.
  109. */
  110. void
  111. main()
  112. {
  113.     int i;
  114.     final_sumL = 0L;
  115.     for (i = 0; i < looptoloopN; i++)
  116.     {
  117.     final_sumL += delay_one(time_oneN);
  118.     final_sumL += delay_two(time_twoN);
  119.     final_sumL += delay_three(time_threeN);
  120.     final_sumL += delay_four(time_fourN);
  121.     final_sumL += delay_five(time_fiveN);
  122.     final_sumL += delay_six(time_sixN);
  123.     final_sumL += delay_seven(time_sevenN);
  124.     final_sumL += delay_eight(time_eightN);
  125.     final_sumL += delay_nine(time_nineN);
  126.     final_sumL += delay_ten(time_tenN);
  127.     final_sumL += delay_eleven(time_elevenN);
  128.     final_sumL += delay_twelve(time_twelveN);
  129.     final_sumL += delay_thirteen(time_thirteenN);
  130.     final_sumL += delay_fourteen(time_fourteenN);
  131.     final_sumL += delay_fifteen(time_fifteenN);
  132.     }
  133. }
  134.  
  135. [LISTING TWO]
  136.  
  137. TITLE   patick - IBM PC / Clone Clock Tick CS:IP Grabber
  138. ; File: patick.asm--Fred Motteler and Applied Microsystems Corporation 
  139. ;   Copyright 1990. All Rights Reserved
  140. ; Description:
  141. ;   This file contains three functions:
  142. ;   C callable:
  143. ;   void painit(bufferLP, lengthN)  Initialize grabber interrupt vector
  144. ;   int paclose()           Close grabber interrupt vector
  145. ;   Interrupt routine, this is treated like part of painit():
  146. ;   patick              Grab CS:IP value
  147. ;   These functions are configured for small model.
  148. ;   Stack frame structure for painit():
  149. ;   
  150. stkfr   STRUC
  151.     OLD_FR  DW  ?   ; Previous stack frame pointer
  152.     RETADDR DW  ?   ; Return address to caller
  153.     BUFFERP DW  ?   ; Pointer to buffer to use
  154.     BUFLEN  DW  ?   ; Length of buffer (in longwords)
  155. stkfr   ENDS
  156. ;
  157. ;   Stack frame structure for clock tick timer routine.
  158. intfr   STRUC
  159.     INT_FR  DW  ?   ; Pre-interrupt stack frame pointer
  160.     IP_VAL  DW  ?   ; Pre-interrupt IP value
  161.     CS_VAL  DW  ?   ; Pre-interrupt CS value
  162. intfr   ENDS
  163. TIMER   EQU 8h      ; Timer interrupt vector number
  164. DGROUP  GROUP   _DATA
  165. _DATA   SEGMENT WORD PUBLIC 'DATA'
  166.         ASSUME  DS:DGROUP
  167. bufptr  DW  0       ; Starting point of buffer
  168. bufsiz  DW  0       ; Number of longwords in the buffer
  169. bufindx DW  0       ; Next location of buffer to use
  170. bufwrap DB  0       ; Flag if buffer has wrapped...
  171. _DATA   ENDS
  172. _TEXT   SEGMENT BYTE PUBLIC 'CODE'
  173.     ASSUME  CS:_TEXT
  174. ;
  175. ;   void paopen (unsigned long *bufferLP, int lengthN)
  176. ;   This a C callable function to initialize the CS:IP grabber and
  177. ;   start it up.  bufferLP points the buffer of where to write CS:IP
  178. ;   values.  lengthN is the length of the buffer in longwords.
  179.     PUBLIC  paopen
  180. paopen  PROC    NEAR
  181.     push    bp
  182.     mov bp,sp
  183.     push    si
  184.     push    di
  185.     push    es
  186. ;
  187. ;   Set up the local buffer pointer values from those passed on the stack.
  188.     mov ax,[bp].BUFFERP ; Get pointer to start of buffer
  189.     mov bufptr,ax
  190.     mov ax,[bp].BUFLEN  ; Get length of the buffer
  191.     shl ax,1        ; convert longword length to byte length
  192.     shl ax,1
  193.     mov bufsiz,ax
  194.     xor ax,ax       ; Start at the beginning of the buffer
  195.     mov bufindx,ax
  196.     mov bufwrap,al  ; Reset buffer wrap flag
  197. ;
  198. ;   Save the original clock tick interrupt vector.
  199.     mov al,TIMER    ; interrupt number into al
  200.     mov ah,35h      ; DOS function = get vector 
  201.     int 21h     ; DOS returns old vector in es:bx
  202.     mov cs:oldseg,es    ; save old segment
  203.     mov cs:oldoff,bx    ; save old offset
  204. ;
  205. ;   Disable interrupts while changing the interrupt vector.
  206.     cli
  207. ;
  208. ;      Change clock tick interrupt routine to point at local interrupt routine.
  209.     mov al,TIMER    ; vector number
  210.     mov ah,25h      ; DOS function = set vector 
  211.     mov dx,OFFSET patick ; point to our interrupt handler
  212.     push    ds      ; don't lose ds, we need to get to local data 
  213.     push    cs      ; move this cs to ds
  214.     pop ds      ; 
  215.     int 21h     ; set the new vector
  216.     pop ds      ; restore ds
  217. ;
  218. ;   Enable interrupts and return;
  219.     pop es
  220.     pop di
  221.     pop si
  222.     pop bp
  223.     sti
  224.     ret
  225. ;
  226. ;   Clock tick grabber routine. This routine samples CS:IP that were pushed
  227. ;       on to the stack when the interrupt occurs.
  228. patick: push    bp
  229.     mov bp,sp       ; Treat CS:IP values like stack frame
  230.     push    ax
  231.     push    bx
  232.     push    ds
  233. ;
  234. ;   Get the local ds to allow access to local variables.
  235.     mov ax,DGROUP
  236.     mov ds,ax
  237. ;
  238. ;   Use bx as a pointer to the recording buffer
  239.     mov bx,bufptr
  240.     add bx,bufindx
  241. ;
  242. ;   Grab the pre-interrupt CS:IP values off the stack
  243.     mov ax,[bp].IP_VAL  ; grab the IP
  244.     mov [bx],ax     ; save the IP in the recording buffer
  245.     inc bx
  246.     inc bx
  247.     mov ax,[bp].CS_VAL  ; grab the CS
  248.     mov [bx],ax     ; save the CS in the recording buffer
  249.     inc bx
  250.     inc bx
  251. ;
  252. ;   Check if we are at the end of the buffer
  253.     sub bx,bufptr   ; get the byte offset index back again
  254.     mov ax,bufsiz   ; get the buffer byte length
  255.     cmp ax,bx
  256.     jne notend      ; jump if not at the end of the buffer
  257. ;
  258. ;   At the end of the buffer
  259.     mov bx,0        ; reset the buffer index
  260.     mov al,0ffh     ; set flag to indicate buffer wrap
  261.     mov bufwrap,al
  262. ;
  263. ;   Write out modified buffer index
  264. notend: mov bufindx,bx
  265. ;
  266. ;   Clean up
  267.     pop ds
  268.     pop bx
  269.     pop ax
  270.     pop bp
  271. ;
  272. ;   Jump to the original interrupt service routine.  An immediate jump
  273. ;   is used so no segment registers are required.
  274.     DB  0eah        ; jmp immediate, to the offset:segment
  275. ;                 selected below (brute force approach).
  276. ;   Original interrupt handler's offset and segment values.  These are
  277. ;   in the current code segment to allow the interrupt routine given
  278. ;   here to directly jump to the original interrupt routine.
  279. oldoff  DW  0       ; Room for original timer interrupt offset
  280. oldseg  DW  0       ; Room for original timer interrupt segment
  281.  
  282. paopen  ENDP
  283. ;
  284. ;   int paclose()  This is a C callable function to close CS:IP grabber and
  285. ;   return the number of CS:IP values grabbed.
  286.     PUBLIC  paclose
  287. paclose PROC    NEAR
  288.     push    bp
  289.     mov bp,sp
  290.     push    si
  291.     push    di
  292.     push    es
  293. ;
  294. ;   Disable interrupts while the original interrupt vector is restored.
  295.     cli
  296.     mov al,TIMER    ; get interrupt number
  297.     mov ah,25h      ; DOS function = set vector 
  298.     push    ds      ;
  299.     mov dx,cs:oldoff    ; old timer offset
  300.     mov ds,cs:oldseg    ; old timer segment
  301.     int 21h     ; restore old vector
  302.     pop ds      ;
  303. ;
  304. ;   Enable interrupts.
  305.     sti
  306. ;
  307. ;   Calculate the number of CS:IP values
  308.     cmp bufwrap,0   ; check if the buffer has wrapped
  309.     jne wrapped     ; jump if it has wrapped
  310.     mov ax,bufindx  ; no wrap, return buffer index as count
  311.     jmp done
  312. wrapped: mov    ax,bufsiz   ; wrapped, return buffer size as count
  313. ;
  314. ;   Clean up stack and return
  315. done:   shr ax,1        ; Return count in number of CS:IP pairs
  316.     shr ax,1
  317.     pop es
  318.     pop di
  319.     pop si
  320.     pop bp
  321.     ret
  322.  
  323. paclose ENDP
  324. _TEXT   ENDS
  325.     END
  326.  
  327.  
  328.  
  329. [LISTING THREE]
  330.  
  331. /* pawhere.c -- contains a very simple program that returns its segment base 
  332. ** address.  Note that this program is Lattice version 6.01 specific in that 
  333. ** the Lattice small model has "main" at the beginning of the exectable 
  334. ** portion of the program. Other compiler/linker packages may require that the
  335. ** program map be examined for the module that starts the program.
  336. ** Copyright 1990 Fred Motteler and Applied Microsystems Corporation
  337. */
  338. #include <dos.h>
  339. #include <stdio.h>
  340.  
  341. unsigned int
  342. main()
  343. {
  344.     FILE *fp;
  345.  
  346.     fp = fopen("pawhere.tmp", "w");
  347.     fprintf(fp, "%x %x\n",
  348.         (FP_SEG((char far *) main)), (FP_OFF((char far *) main)));
  349.     fclose(fp);
  350.     exit(0);
  351. }
  352.  
  353.  
  354.  
  355. [LISTING FOUR]
  356.  
  357. /* pamsdos.c -- Utility functions used by MS-DOS version of the statistical 
  358. ** performance analysis package. 
  359. ** Copyright 1990 Fred Motteler and Applied Microsystems Corporation
  360. */
  361.  
  362. #include <stdio.h>
  363. #include <stdlib.h>
  364. #include <fcntl.h>
  365. #include <string.h>
  366. #include "padef.h"
  367.  
  368. /* Function: int main( argcN, argvAS )
  369. ** Description: MS-DOS based statistical performance analysis program.
  370. **   Command line arguments: pamsdos prog.map prog.cfg prog.exe options
  371. **   Where: prog.map = memory map for program; prog.cfg = memory map 
  372. **    configuration; prog.exe = program to run; options = command options 
  373. **    for the program to run
  374. */    
  375. int
  376. main( argcN, argvAS )
  377. int argcN;
  378. char *argvAS[];
  379. {
  380.     int errorN;             /* Error code */
  381.     unsigned int segmentW;  /* Starting load address of program to run */
  382.     unsigned int offsetW;
  383.     unsigned long originL;
  384.     int processedN;         /* Number of map globals processed */
  385.     int i;                  /* General index */
  386.     FILE *mapFP;            /* Map file to read */
  387.     FILE *formatFP;         /* File with map file format information */
  388.     char commandAC[PA_LINE_LEN]; /* Complete command line for program */
  389.     int pagelinesN;         /* Number of lines on output page, 0 if
  390.                  * continuous, -1 if no display output, else
  391.                  * n if n lines per page. */
  392.     FILE *listFP;           /* Results output file */
  393.     char listAB[80];        /* Optional results listing file path/name */
  394.     char pagelinesAB[8];    /* String for number of lines/page */
  395.  
  396.     printf("pamsdos - Statistical performance analysis tool for MS-DOS\n");
  397.     printf("Version %s\n", PA_VERSION);
  398.     printf("Copyright (C) 1990 Fred Motteler and Applied Microsystems Corp\n");
  399.     if (argcN < 4)
  400.     {
  401.     printf("Usage: pamsdos prog.map prog.cfg prog.exe [options]\n");
  402.     printf(" Where: prog.map  memory map for program\n");
  403.     printf("        prog.cfg  memory map configuration file\n");
  404.     printf("        prog.exe  program to run\n");
  405.     printf("        [options] command line options for program to run\n");
  406.     exit(-100);
  407.     }
  408.  
  409.     /* Determine where the program to run is to be located. */
  410.     if ((errorN = pa_locate(&segmentW, &offsetW)) != 0)
  411.     {
  412.     pa_error(errorN);
  413.     exit(errorN);
  414.     }
  415.     /* Calculate origin of program.  Room must be allowed for memory
  416.      * malloc()'d off the heap. */
  417.     originL = (unsigned long) (segmentW + 1);
  418.     originL <<= 4;
  419.     originL += (unsigned long) (offsetW - 2);
  420.     originL += (unsigned long) (PA_BUFLEN << 2);
  421.  
  422.     if ((pa_debugN & PA_GENERAL) != 0)
  423.     {
  424.     printf("program start segment:offset %x:%x\n", segmentW, offsetW);
  425.     printf("              linear address %lx\n",originL);
  426.     }
  427.  
  428.     /* Get the complete command line to invoke the program. */
  429.     strcpy(commandAC, argvAS[3]);
  430.     if (argcN > 4)
  431.     {
  432.     for (i = 4; i <  argcN; i++)
  433.     {
  434.         strcat(commandAC," ");
  435.         strcat(commandAC,argvAS[i]);
  436.     }
  437.     }
  438.  
  439.     /* Run the program and collect samples. */
  440.     printf("Starting %s\n", argvAS[3]);
  441.     if ((errorN = pa_pcsample(commandAC, PA_SAMPLE, PA_BUFLEN)) != 0)
  442.     {
  443.     pa_error(errorN);
  444.     exit(errorN);
  445.     }
  446.  
  447.     /* Read in the configuration file to get map format information and
  448.      * to get number of lines / display page and option listing file. */
  449.     if ((formatFP = fopen(argvAS[2], "r")) == (FILE *) NULL)
  450.     {
  451.     pa_error(PA_NO_CFG_E);
  452.     exit(PA_NO_CFG_E);
  453.     }
  454.  
  455.     /* Read in display lines, and optional output file configuration data 
  456.      * from the configuration file. */
  457.     if (((errorN = paconfig(formatFP, PA_PAGELINES, pagelinesAB)) != 0) ||
  458.     ((errorN = paconfig(formatFP, PA_LISTFILE, listAB)) != 0))
  459.     {
  460.     pa_error(errorN);
  461.     fclose(formatFP);
  462.     exit(errorN);
  463.     }
  464.  
  465.     /* Determine the number of lines/page to display */
  466.     if (sscanf(pagelinesAB, "%d", &pagelinesN) != 1)
  467.     {
  468.     pa_error(PA_BAD_ARG_E);
  469.     fclose(formatFP);
  470.     exit(PA_BAD_ARG_E);
  471.     }
  472.  
  473.     /* Open the optional listing file */
  474.     if (listAB[0] == '\0')
  475.     listFP = (FILE *) NULL;
  476.     else if ((listFP = fopen(listAB, "w")) == (FILE *) NULL)
  477.     {
  478.     pa_error(PA_NO_LST_E);
  479.     fclose(formatFP);
  480.     exit(PA_NO_LST_E);
  481.     }
  482.  
  483.  /* Read program's memory map and create "bins" for program counter samples. */
  484.     if ((mapFP = fopen(argvAS[1], "r")) == (FILE *) NULL)
  485.     {
  486.     pa_error(PA_NO_MAP_E);
  487.     fclose(mapFP);
  488.     exit(PA_NO_MAP_E);
  489.     }
  490.     if ((errorN = pardmap(mapFP, formatFP, originL, &processedN)) != 0)
  491.     {
  492.     pa_error(errorN);
  493.     fclose(mapFP);
  494.     fclose(formatFP);
  495.     exit(errorN);
  496.     }
  497.  
  498.     /* Process the samples and sort the bins according to the PC hits in
  499.      * each bin. */
  500.     printf("Processing samples\n");
  501.     if ((errorN = pa_bstuff(PA_SAMPLE, patableAHP, &processedN)) != 0)
  502.     {
  503.     pa_error(errorN);
  504.     fclose(mapFP);
  505.     fclose(formatFP);
  506.     exit(errorN);
  507.     }
  508.  
  509.     /* Display the results */
  510.     padisply(patableAHP, processedN, pagelinesN, listFP);
  511.     fclose(mapFP);
  512.     fclose(formatFP);
  513.     exit(0);
  514. }
  515.  
  516. /* Function: int pa_locate(unsigned int *segmentPW, unsigned int *offsetPW)
  517. ** Description: This function figures out where in memory the program to be
  518. **  analyzed is to be run. MS-DOS executables are dynamically located at 
  519. **  runtime. In order to avoid the complexity of writing a DOS ".exe" loader 
  520. **  program, a simpler approach is used here. This function uses the ANSI 
  521. **  system() library function to execute a trial program, "pawhere.exe" that 
  522. **  writes its starting code segment and offset to a temporary file 
  523. **  "pawhere.tmp". After "pawhere.exe" has finished, this function opens the 
  524. **  temporary file and reads the starting segment and offset value. It is 
  525. **  assumed that the desired program to be tested will have the same starting 
  526. **  code segment and offset. If all operations were successful, then 0 is 
  527. **  returned. Otherwise a non-zero error code will be returned.*/ 
  528. int
  529. pa_locate(segmentPW, offsetPW)
  530. unsigned int *segmentPW;
  531. unsigned int *offsetPW;
  532. {
  533.     FILE *fp;
  534.  
  535.     /* First figure out where the program will be loaded.  Run "pawhere.exe"
  536.      * via a system() function call. */
  537.     if ((system("pawhere")) != 0)
  538.     return(PA_NO_WHERE_E);
  539.  
  540.     /* Read in the result of whereami.tmp. */
  541.     if ((fp = fopen("pawhere.tmp", "r")) == (FILE *) NULL)
  542.     return(PA_NO_TMP_E);
  543.     if ((fscanf(fp, "%x %x", segmentPW, offsetPW)) != 2)
  544.     return(PA_BAD_TMP_E);
  545.     fclose(fp);
  546.     if (remove("pawhere.tmp") != 0)
  547.     return(PA_TMP_RM_E);
  548.  
  549.     return(0);
  550. }
  551.  
  552. /* Function: int pa_pcsample(char *programS, char *sampfileS, int samplesN)
  553. ** Description: This function runs the program (entire command line) pointed
  554. **   to by programS, while sampling its program counter every PC clock tick.  
  555. **   Up to samplesN program counter samples are collected, and then written 
  556. **   out in binary format to the file sampfiles.*/
  557. int
  558. pa_pcsample(programS, sampfileS, samplesN)
  559. char *programS;         /* Command line of program to run */
  560. char *sampfileS;        /* File to use to write out pc samples */
  561. int samplesN;           /* Maximum number of samples to collect */
  562. {
  563.     unsigned long *pcbufferPL;  /* Word pointer to local pc sample buffer */
  564.     unsigned int *pcbufferPW;   /* Long pointer to local pc sample buffer */
  565.     unsigned long *pcorgPL; /* Original copy of pointer to pc sample buf */
  566.     unsigned int segmentW;  /* Starting segment of program to run */
  567.     unsigned int offsetW;   /* Starting offset of program to run */
  568.     int handleN;        /* pc sample file handle */
  569.     unsigned long sampleL;  /* segment:offset sample converted to linear */
  570.     int i;          /* general index */
  571.  
  572.     /* Grab memory for the sample buffer */
  573.     if ((pcbufferPL = (unsigned long *) malloc((4*samplesN)))
  574.     == (unsigned long *) NULL)
  575.     return(PA_NO_MEM_E);
  576.     /* Copy buffer pointer to allow word (int) access as well as long access.*/
  577.     pcbufferPW = (unsigned int *) pcbufferPL;
  578.     pcorgPL = pcbufferPL;
  579.  
  580.     /* Start CS:IP sampling */
  581.     paopen(pcbufferPW, samplesN);
  582.  
  583.     /* Run the desired program. */
  584.     if (system(programS) != 0)
  585.     {
  586.     paclose();
  587.     return(PA_NO_EXEC_E);
  588.     }
  589.  
  590.     /* Stop sampling */
  591.     samplesN = paclose();
  592.  
  593.     /* Convert the samples from offsetW:segment to linear addresses relative
  594.      * to the origin of the loaded program. */
  595.     if ((pa_debugN & PA_GENERAL) != 0)
  596.     printf("pa_pcsample: number of samples: %d\n", samplesN);
  597.     for (i = 0; i < samplesN; i++)
  598.     {
  599.     /* Read segment:offset value from the table. */
  600.     offsetW = *pcbufferPW++;
  601.     segmentW = *pcbufferPW++;
  602.  
  603.     if ((pa_debugN & PA_GENERAL) != 0)
  604.         printf("pa_pcsample: sample segment:offset %x:%x\n",
  605.             segmentW,offsetW);
  606.  
  607.     /* Convert it to a linear address. */
  608.     sampleL = ((unsigned long) offsetW)
  609.         + (((unsigned long) segmentW) << 4);
  610.     /* Write the linear address back to the table. */
  611.     *pcbufferPL++ = sampleL;
  612.     if ((pa_debugN & PA_GENERAL) != 0)
  613.         printf("pa_pcsample: linear sample %lx\n",sampleL);
  614.     }
  615.  
  616.     /* Write the samples to a binary file. */
  617.     if ((handleN = open (sampfileS, (O_CREAT | O_WRONLY | O_RAW), 0))
  618.     == (-1))
  619.     {
  620.     free(pcorgPL);
  621.     return(PA_NO_PC_FILE_E);
  622.     }
  623.     if ((write( handleN, ((char *) pcorgPL), (samplesN << 2)))
  624.     != (samplesN << 2))
  625.     {
  626.     close(handleN);
  627.     free(pcorgPL);
  628.     return(PA_NO_PC_WR_E);
  629.     }
  630.  
  631.     close(handleN);
  632.     free(pcorgPL);
  633.     return(0);
  634. }
  635.  
  636.  
  637.